package 加密与安全;

/*
哈希算法（Hash）又称摘要算法（Digest），它的作用是：对任意一组输入数据进行计算，得到一个固定长度的输出摘要。

哈希算法最重要的特点就是：
 -相同的输入一定得到相同的输出；
 -不同的输入大概率得到不同的输出。
哈希算法的目的就是为了验证原始数据是否被篡改。

哈希碰撞是指，两个不同的输入得到了相同的输出

常用的哈希算法有：

算法	          输出长度（位）         输出长度（字节）
MD5	       128 bits           16 bytes
SHA-1        160 bits           20 bytes
RipeMD-160   160 bits           20 bytes
SHA-256      256 bits           32 bytes
SHA-512      512 bits           64 bytes

MD5:message digest algorithm 5 信息摘要算法
SHA:secure hash algorithm 安全散列算法
HMAC:Hash-based Message Authentication Code 散列消息验证码

*/
import java.math.BigInteger;
import java.security.MessageDigest;

public class 哈希算法 {

	public static void main(String[] args) throws Exception {
      // 创建一个MessageDigest实例:
      MessageDigest md = MessageDigest.getInstance("MD5");
      // 反复调用update输入数据:  更新原始内容
      md.update("Hello".getBytes("UTF-8"));
      md.update("World".getBytes("UTF-8"));
      byte[] result = md.digest(); // 16 bytes: 68e109f0f40ca72a15e05cc22786f8e6
      System.out.println(new BigInteger(1, result).toString(16));
      
      // 创建一个MessageDigest实例:
      MessageDigest md1 = MessageDigest.getInstance("SHA-1");
      // 反复调用update输入数据:
      md1.update("Hello".getBytes("UTF-8"));
      md1.update("World".getBytes("UTF-8"));
      byte[] result1 = md1.digest(); // 20 bytes: db8ac1c259eb89d4a131b253bacfca5f319d54f2
      System.out.println(new BigInteger(1, result1).toString(16));
      
   // 创建一个MessageDigest实例:
      MessageDigest md2 = MessageDigest.getInstance("SHA-256");
      // 反复调用update输入数据:
      md2.update("Hello".getBytes("UTF-8"));
      md2.update("World".getBytes("UTF-8"));
      byte[] result2 = md2.digest(); // 32 bytes: 872e4e50ce9990d8b041330c47c9ddd11bec6b503ae9386a99da8584e9bb12c4
      System.out.println(new BigInteger(1,result2).toString(16));
  }
}

/*
使用哈希口令时，还要注意防止彩虹表攻击。

什么是彩虹表呢？上面讲到了，如果只拿到MD5，从MD5反推明文口令，只能使用暴力穷举的方法。

然而黑客并不笨，暴力穷举会消耗大量的算力和时间。但是，如果有一个预先计算好的常用口令和它们的MD5的对照表：

常用口令	   MD5
hello123	f30aa7a662c728b7407c54ae6bfd27d1
12345678	25d55ad283aa400af464c76d713c07ad
passw0rd	bed128365216c019988915ed3add75fb
19700101	570da6d5277a646f6552b8832012f5dc
…	…
20201231	6879c0ae9117b50074ce0a0d4c843060
这个表就是彩虹表。如果用户使用了常用口令，黑客从MD5一下就能反查到原始口令：

bob的MD5：  f30aa7a662c728b7407c54ae6bfd27d1，原始口令：hello123；
alice的MD5：25d55ad283aa400af464c76d713c07ad，原始口令：12345678；
tim的MD5：  bed128365216c019988915ed3add75fb，原始口令：passw0rd。

这就是为什么不要使用常用密码，以及不要使用生日作为密码的原因。

即使用户使用了常用口令，我们也可以采取措施来抵御彩虹表攻击，方法是对每个口令额外添加随机数，这个方法称之为加盐（salt）：

digest = md5(salt+inputPassword)
经过加盐处理的数据库表，内容如下：

username	salt	  password
bob	   H1r0a	  a5022319ff4c56955e22a74abcc2c210
alice	   7$p2w	  e5de688c99e961ed6e560b972dab8b6a
tim	   z5Sk9	  1eee304b92dc0d105904e7ab58fd2f64
加盐的目的在于使黑客的彩虹表失效，即使用户使用常用口令，也无法从MD5反推原始口令。


HMAC算法的一个典型应用是用在“挑战/响应”（Challenge/Response）身份认证中，认证流程如下：
(1) 先由客户端向服务器发出一个验证请求。
(2) 服务器接到此请求后生成一个随机数并通过网络传输给客户端（此为挑战）。
(3) 客户端将收到的随机数与自己的密钥进行HMAC-SHA1运算并得到一个结果作为认证证据传给服务器（此为响应）。
(4) 与此同时，服务器也使用该随机数与存储在服务器数据库中的该客户密钥进行HMAC-SHA1运算，如果服务器的运算结果与客户端传回的响应结果
    相同，则认为客户端是一个合法用户 。

*/